home *** CD-ROM | disk | FTP | other *** search
- /* Copyright (c) 1990,1991,1992 Chris and John Downey */
- #ifndef lint
- static char *sccsid = "@(#)find.c 2.2 (Chris & John Downey) 7/29/92";
- #endif
-
- /***
-
- * program name:
- xvi
- * function:
- PD version of UNIX "vi" editor, with extensions.
- * module name:
- find.c
- * module function:
- Character and function searching.
- * history:
- STEVIE - ST Editor for VI Enthusiasts, Version 3.10
- Originally by Tim Thompson (twitch!tjt)
- Extensive modifications by Tony Andrews (onecom!wldrdg!tony)
- Heavily modified by Chris & John Downey
-
- ***/
-
- #include "xvi.h"
- #include "regexp.h" /* Henry Spencer's regular expression routines */
-
- #ifdef MEGAMAX
- overlay "find"
- #endif
-
- /*
- * Character Searches
- */
-
- static char lastc; /* last character searched for */
- static int lastcdir; /* last direction of character search */
- static int lastctype; /* last type of search ("find" or "to") */
-
- /*
- * searchc(c, dir, type, num)
- *
- * Search for character 'c', in direction 'dir'. If type is 0, move to
- * the position of the character, otherwise move to just before the char.
- * 'num' is the number of times to do it (the prefix number).
- */
- Posn *
- searchc(ch, dir, type, num)
- int ch;
- int dir;
- int type;
- int num;
- {
- static Posn pos; /* saved cursor posn */
- enum mvtype (*mvfunc) P((Posn *));
- enum mvtype (*backfunc) P((Posn *));
- unsigned char c; /* as ch but uchar */
- register int i; /* loop counter */
-
- /*
- * Remember details in case we want to repeat the search.
- */
- lastc = ch;
- lastcdir = dir;
- lastctype = type;
-
- pos = *(curwin->w_cursor); /* save position in case we fail */
- c = ch;
- if (dir == FORWARD) {
- mvfunc = inc;
- backfunc = dec;
- } else {
- mvfunc = dec;
- backfunc = inc;
- }
-
- /*
- * On 'to' searches, skip one to start with so we can repeat
- * searches in the same direction and have it work right.
- */
- if (type) {
- if ((*mvfunc)(&pos) != mv_SAMELINE) {
- return(NULL);
- }
- }
-
- for (i = 0; i < num; i++) {
- bool_t found;
-
- found = FALSE;
- while ((*mvfunc)(&pos) == mv_SAMELINE) {
- if ((unsigned char) gchar(&pos) == c) {
- found = TRUE;
- break;
- }
- }
- if (!found) {
- return(NULL);
- }
- }
- if (type) {
- (void) (*backfunc)(&pos);
- }
- return(&pos);
- }
-
- /*ARGSUSED*/
- Posn *
- crepsearch(buffer, flag, num)
- Buffer *buffer;
- int flag;
- int num;
- {
- Posn *newpos;
- int dir;
- int savedir;
-
- if (lastc == '\0') {
- return(NULL);
- }
-
- savedir = lastcdir;
- if (flag) {
- dir = (lastcdir == FORWARD) ? BACKWARD : FORWARD;
- } else {
- dir = lastcdir;
- }
-
- newpos = searchc(lastc, dir, lastctype, num);
-
- lastcdir = savedir; /* put direction of search back how it was */
-
- return(newpos);
- }
-
- /*
- * "Other" Searches
- */
-
- /*
- * showmatch - move the cursor to the matching paren or brace
- */
- Posn *
- showmatch()
- {
- register char initc; /* initial char */
- register enum mvtype
- (*move) P((Posn *)); /* function to move cursor */
- register char findc; /* terminating char */
- static Posn pos; /* current position */
- char c;
- int count = 0;
- bool_t found = FALSE;
-
- pos = *curwin->w_cursor;
-
- /*
- * Move forward to the first bracket character after the cursor.
- * If we get to EOF before a bracket, return NULL.
- */
- for (found = FALSE; !found; ) {
- initc = gchar(&pos);
- switch (initc) {
- case '(':
- findc = ')';
- move = inc;
- found = TRUE;
- break;
- case ')':
- findc = '(';
- move = dec;
- found = TRUE;
- break;
- case '{':
- findc = '}';
- move = inc;
- found = TRUE;
- break;
- case '}':
- findc = '{';
- move = dec;
- found = TRUE;
- break;
- case '[':
- findc = ']';
- move = inc;
- found = TRUE;
- break;
- case ']':
- findc = '[';
- move = dec;
- found = TRUE;
- break;
- default:
- if (inc(&pos) == mv_NOMOVE) {
- return(NULL);
- }
- }
- }
-
- /*
- * Move in the appropriate direction until we find a matching
- * bracket or reach end of file.
- */
- while ((*move)(&pos) != mv_NOMOVE) {
- c = gchar(&pos);
- if (c == initc) {
- count++;
- } else if (c == findc) {
- if (count == 0)
- return(&pos);
- count--;
- }
- }
- return(NULL); /* never found it */
- }
-
- /*
- * Find the nth next occurrence of str in the specified direction.
- */
- Posn *
- find_pattern(str, dir, num)
- char *str;
- int dir;
- int num;
- {
- Posn *p;
- Posn *lastp;
-
- p = curwin->w_cursor;
- lastp = NULL;
- while (num-- > 0) {
- p = nsearch(curwin, p->p_line, p->p_index, dir, str);
- if (p != NULL) {
- lastp = p;
- } else {
- break;
- }
- }
-
- return(lastp);
- }
-
- /*
- * The following routines do the word searches performed by the
- * 'w', 'W', 'b', 'B', 'e', and 'E' commands.
- */
-
- /*
- * To perform these searches, characters are placed into one of three
- * classes, and transitions between classes determine word boundaries.
- *
- * The classes are:
- *
- * cl_white - white space
- * cl_text - letters, digits, and underscore
- * cl_punc - everything else
- */
-
- typedef enum {
- cl_white,
- cl_text,
- cl_punc
- } cclass;
-
- static int stype; /* type of the word motion being performed */
-
- #define is_white(c) (((c) == ' ') || ((c) == '\t') || ((c) == '\0'))
- #define is_text(c) (is_alnum(c) || ((c) == '_'))
-
- /*
- * cls(c) - returns the class of character 'c'
- *
- * The 'type' of the current search modifies the classes of characters
- * if a 'W', 'B', or 'E' motion is being done. In this case, chars. from
- * class "cl_punc" are reported as class "cl_text" since only white space
- * boundaries are of interest.
- */
- static cclass
- cls(c)
- char c;
- {
- if (is_white(c))
- return(cl_white);
-
- if (is_text(c))
- return(cl_text);
-
- /*
- * If stype is non-zero, report these as class 1.
- */
- return((stype == 0) ? cl_punc : cl_text);
- }
-
-
- /*
- * fwd_word(pos, type, skip_white) - move forward one word
- *
- * Returns the resulting position, or NULL if EOF was reached.
- *
- * The extra argument "skip_white" (which is only used in fwd_word,
- * but also included in bck_word and end_word for compatibility) is
- * used to indicate whether to skip over white space to get to the
- * start of the next word. If it is FALSE, the position returned will
- * be the first white-space character (or punctuation) encountered.
- * This is used by one command only: "cw".
- */
- Posn *
- fwd_word(p, type, skip_white)
- Posn *p;
- int type;
- bool_t skip_white;
- {
- static Posn pos;
- cclass sclass; /* starting class */
-
- stype = type;
- sclass = cls(gchar(p));
-
- pos = *p;
-
- /*
- * We always move at least one character.
- */
- if (inc(&pos) == mv_NOMOVE)
- return(NULL);
-
- if (sclass != cl_white) {
- /*
- * We were in the middle of a word to start with.
- * Move right until we change character class.
- */
- while (cls(gchar(&pos)) == sclass) {
- if (inc(&pos) == mv_NOMOVE) {
- /*
- * Got to EOF. Return current position.
- */
- return(&pos);
- }
- }
-
- /*
- * If we went from punctuation -> text
- * or text -> punctuation, return here.
- *
- * If we went text/punctuation -> whitespace,
- * we want to continue to the start of the
- * next word, if there is one.
- */
- if (cls(gchar(&pos)) != cl_white)
- return(&pos);
- }
-
- /*
- * We're in white space; go to next non-white.
- */
- if (skip_white) {
- while (cls(gchar(&pos)) == cl_white) {
- /*
- * We'll stop if we land on a blank line
- */
- if (pos.p_index == 0 && pos.p_line->l_text[0] == '\0')
- break;
-
- if (inc(&pos) == mv_NOMOVE) {
- /*
- * We have reached end of file; if we are at
- * the beginning of a line, just return that
- * position, otherwise try to back up so that
- * we are still within the line.
- */
- if (pos.p_index != 0) {
- (void) dec(&pos);
- }
- break;
- }
- }
-
- /*
- * If we didn't move, return NULL.
- */
- if (pos.p_line == p->p_line && pos.p_index == p->p_index) {
- return(NULL);
- }
- }
-
- return(&pos);
- }
-
- /*
- * bck_word(pos, type, skip_white) - move backward one word
- *
- * Returns the resulting position, or NULL if EOF was reached.
- */
- /*ARGSUSED*/
- Posn *
- bck_word(p, type, skip_white)
- Posn *p;
- int type;
- bool_t skip_white;
- {
- static Posn pos;
- cclass sclass; /* starting class */
-
- stype = type;
- sclass = cls(gchar(p));
-
- pos = *p;
-
- if (dec(&pos) == mv_NOMOVE)
- return(NULL);
-
- /*
- * If we're in the middle of a word, we just have to
- * back up to the start of it.
- */
- if (cls(gchar(&pos)) == sclass && sclass != cl_white) {
- /*
- * Move backward to start of the current word
- */
- while (cls(gchar(&pos)) == sclass) {
- if (dec(&pos) == mv_NOMOVE)
- return(&pos);
- }
- (void) inc(&pos); /* overshot - forward one */
- return(&pos);
- }
-
- /*
- * We were at the start of a word. Go back to the start
- * of the prior word.
- */
-
- while (cls(gchar(&pos)) == cl_white) { /* skip any white space */
- /*
- * We'll stop if we land on a blank line
- */
- if (pos.p_index == 0 && pos.p_line->l_text[0] == '\0')
- return(&pos);
-
- if (dec(&pos) == mv_NOMOVE)
- return(&pos);
- }
-
- sclass = cls(gchar(&pos));
-
- /*
- * Move backward to start of this word.
- */
- while (cls(gchar(&pos)) == sclass) {
- if (dec(&pos) == mv_NOMOVE)
- return(&pos);
- }
- (void) inc(&pos); /* overshot - forward one */
-
- return(&pos);
- }
-
- /*
- * end_word(pos, type, skip_white) - move to the next end-of-word after
- * the current cursor position
- *
- * There is an apparent bug in the 'e' motion of the real vi. At least
- * on the System V Release 3 version for the 80386. Unlike 'b' and 'w',
- * the 'e' motion crosses blank lines. When the real vi crosses a blank
- * line in an 'e' motion, the cursor is placed on the FIRST character
- * of the next non-blank line. The 'E' command, however, works correctly.
- * Since this appears to be a bug, I have not duplicated it here.
- *
- * Returns the resulting position, or NULL if EOF was reached.
- */
- /*ARGSUSED*/
- Posn *
- end_word(p, type, skip_white)
- Posn *p;
- int type;
- bool_t skip_white;
- {
- static Posn pos;
- cclass sclass;
-
- stype = type;
- sclass = cls(gchar(p));
-
- pos = *p;
-
- if (inc(&pos) == mv_NOMOVE)
- return(NULL);
-
- /*
- * If we're in the middle of a word, we just have to
- * move to the end of it.
- */
- if (cls(gchar(&pos)) == sclass && sclass != cl_white) {
- /*
- * Move forward to end of the current word
- */
- while (cls(gchar(&pos)) == sclass) {
- if (inc(&pos) == mv_NOMOVE) {
- return(&pos);
- }
- }
- (void) dec(&pos); /* overshot - forward one */
- return(&pos);
- }
-
- /*
- * We were at the end of a word. Go to the end
- * of the next word.
- */
-
- while (cls(gchar(&pos)) == cl_white) { /* skip any white space */
- if (inc(&pos) == mv_NOMOVE) {
- return(&pos);
- }
- }
-
- sclass = cls(gchar(&pos));
-
- /*
- * Move forward to end of this word.
- */
- while (cls(gchar(&pos)) == sclass) {
- if (inc(&pos) == mv_NOMOVE) {
- return(&pos);
- }
- }
- (void) dec(&pos); /* overshot - forward one */
-
- return(&pos);
- }
-
- /*
- * Search for the given pattern in the given buffer,
- * in the direction specified.
- */
- bool_t
- dosearch(window, str, cmd_char)
- Xviwin *window;
- char *str;
- int cmd_char;
- {
- Posn *p;
- unsigned savecho;
- bool_t retval;
- int dir;
- static int lastdir = FORWARD;
-
- /*
- * Place the cursor at bottom left of the window,
- * so the user knows what we are doing.
- */
- gotocmd(window, FALSE);
-
- switch (cmd_char) {
- case '/':
- lastdir = dir = FORWARD;
- break;
- case '?':
- lastdir = dir = BACKWARD;
- break;
- case 'n':
- dir = lastdir;
- break;
- case 'N':
- dir = (lastdir == FORWARD) ? BACKWARD : FORWARD;
- break;
- }
-
- /*
- * It is safe not to put the cursor back, because
- * we are going to produce some more output anyway.
- */
-
- savecho = echo;
-
- p = search(window, window->w_cursor->p_line,
- window->w_cursor->p_index, dir, &str);
- if (p == NULL) {
- regerror("Pattern not found");
- retval = FALSE;
- } else if (*str != '\0') {
- regerror("Usage: /pattern or ?pattern");
- retval = FALSE;
- } else {
- setpcmark(window);
- move_cursor(window, p->p_line, p->p_index);
- window->w_set_want_col = TRUE;
- retval = TRUE;
- }
-
- echo = savecho;
- return retval;
- }
-